﻿/*
 * This file is part of MonoStrategy.
 *
 * Copyright (C) 2010-2011 Christoph Husse
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Authors: 
 *      # Christoph Husse
 * 
 * Also checkout our homepage: http://monostrategy.codeplex.com/
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace MonoStrategy
{
    public enum SettlerProfessions
    {
        Geologist,
        Pioneer,
        Agent,
        Grader,
        Constructor,

        Max, // must be the last entry
    }

    public enum SettlerStatisticTypes
    {
        Settler,
        Geologist,
        Pioneer,
        Agent,
        Grader,
        Constructor,
        Speer,
        Swordsman,
        Archer,
        Priest,
        Worker,

        Max, // must be the last entry
    }

    public partial class GameMap
    {
        public class GameConfig
        {
            public class StockQuantityArray
            {
                private readonly int[] m_Counts = new int[(int)Resource.Max];

                public int this[Resource index]
                {
                    get
                    {
                        return m_Counts[(int)index];
                    }
                }

                internal void Update(GameMap inMap)
                {
                    inMap.ResMgr.CountResources(m_Counts);
                }
            }

            public class SettlerTypeCountArray
            {
                private readonly int[] m_Counts = new int[(int)SettlerStatisticTypes.Max];

                public int this[SettlerStatisticTypes index]
                {
                    get
                    {
                        return m_Counts[(int)index];
                    }
                }

                internal void Update(GameConfig inConfig)
                {
                    inConfig.Map.MovMgr.CountMovables(m_Counts);
                }
            }

            public class ToolConfig
            {
                private double m_Percentage;
                private int m_Todo;
                private GameConfig m_Config;

                public int Todo
                {
                    get { return m_Todo; }
                    internal set
                    {
                        m_Todo = Math.Max(0, Math.Min(8, value));
                    }
                }
                public double Percentage
                {
                    get { return m_Percentage; }
                    internal set
                    {
                        m_Percentage = Math.Max(0, Math.Min(1.0, value));
                    }
                }

                internal ToolConfig(GameConfig inConfig)
                {
                    if (inConfig == null)
                        throw new ArgumentNullException();

                    m_Config = inConfig;
                }
            }

            public class ToolsArray
            {
                private readonly ToolConfig[] m_Tools = new ToolConfig[(int)Resource.Max];

                internal ToolsArray(GameConfig inConfig)
                {
                    for (int i = 0; i < m_Tools.Length; i++)
                    {
                        switch ((Resource)i)
                        {
                            case Resource.Axe:
                            case Resource.Bow:
                            case Resource.Hammer:
                            case Resource.Hook:
                            case Resource.PickAxe:
                            case Resource.Saw:
                            case Resource.Scythe:
                            case Resource.Shovel:
                            case Resource.Spear:
                            case Resource.Sword:
                                break;
                            default:
                                continue;
                        }

                        m_Tools[i] = new ToolConfig(inConfig);
                    }
                }

                public ToolConfig this[Resource tool]
                {
                    get
                    {
                        var res = m_Tools[(int)tool];

                        if (res == null)
                            throw new ArgumentOutOfRangeException("The given resource is not a tool!");

                        return res;
                    }
                }
            }

            public class StockDistribution
            {
                public class QueryArray
                {
                    private readonly bool[] m_Queries = new bool[(int)Resource.Max];

                    public bool this[Resource index]
                    {
                        get { return m_Queries[(int)index]; }
                        internal set { m_Queries[(int)index] = value; }
                    }
                }

                public BuildingConfig Building { get; private set; }
                public QueryArray Queries { get; private set; }

                internal StockDistribution(BuildingConfig inBuilding)
                {
                    if (inBuilding == null)
                        throw new ArgumentNullException();

                    Building = inBuilding;
                    Queries = new QueryArray();
                }
            }

            public class StockDistributionArray
            {
                private readonly StockDistribution[] m_Dists;
                private GameMap m_Map;

                public StockDistribution this[int index]
                {
                    get
                    {
                        return m_Dists[index];
                    }
                }

                public int Length { get { return m_Dists.Length; } }

                internal StockDistributionArray(GameMap inMap)
                {
                    List<StockDistribution> distList = new List<StockDistribution>();

                    if (inMap == null)
                        throw new ArgumentNullException();

                    m_Map = inMap;

                    // currently only MineBuildings are eligable for distribution settings (just makes no sense to include other buildings, if
                    // you are not trying to workaround flaws within your economy)
                    foreach (var mine in m_Map.Race.Buildables.Where(e => typeof(MineBuilding).IsAssignableFrom(e.ClassType)))
                    {
                        var dist = new StockDistribution(mine);
                        distList.Add(dist);

                        // select the resource query with highest quality food, we will enable it by default...
                        var bestFoodStack = mine.ResourceStacks.Where(e => e.Direction == ResStackType.Query).OrderBy(e => e.QualityIndex).First();

                        dist.Queries[bestFoodStack.Type] = true;
                    }

                    m_Dists = distList.ToArray();
                }

            }

            private readonly Object m_Lock = new object();

            public StockDistributionArray StockDistributions { get; private set; }
            public StockQuantityArray StockQuantities { get; private set; }
            public SettlerTypeCountArray SettlerTypeCounts { get; private set; }
            public ToolsArray Tools { get; private set; }
            public int HouseSpaceCount { get; private set; }
            public int SettlerCount { get; private set; }
            public int SoldierCount { get; private set; }
            public GameMap Map { get; private set; }

            internal GameConfig(GameMap inParent)
            {
                if (inParent == null)
                    throw new ArgumentNullException();

                Map = inParent;
                StockDistributions = new StockDistributionArray(inParent);
                StockQuantities = new StockQuantityArray();
                SettlerTypeCounts = new SettlerTypeCountArray();
                Tools = new ToolsArray(this);

                Update();
            }

            internal void Update()
            {
                SettlerTypeCounts.Update(this);
                StockQuantities.Update(Map);

                HouseSpaceCount = Map.BuildMgr.ComputeHouseSpaceCount();
                SoldierCount =
                    SettlerTypeCounts[SettlerStatisticTypes.Speer] +
                    SettlerTypeCounts[SettlerStatisticTypes.Swordsman] +
                    SettlerTypeCounts[SettlerStatisticTypes.Archer];
                SettlerCount =
                    SettlerTypeCounts[SettlerStatisticTypes.Settler] +
                    SettlerTypeCounts[SettlerStatisticTypes.Worker] +
                    SettlerTypeCounts[SettlerStatisticTypes.Grader] +
                    SettlerTypeCounts[SettlerStatisticTypes.Constructor] +
                    SettlerTypeCounts[SettlerStatisticTypes.Agent] +
                    SettlerTypeCounts[SettlerStatisticTypes.Geologist] +
                    SettlerTypeCounts[SettlerStatisticTypes.Pioneer];
            }

            internal void ChangeToolSetting(Resource inTool, int inNewTodo, double inNewPercentage)
            {
                Tools[inTool].Todo = inNewTodo;
                Tools[inTool].Percentage = inNewPercentage;
            }

            internal void ChangeDistributionSetting(String inBuildingClass, Resource inResource, bool inIsEnabled)
            {
                for (int i = 0; i < StockDistributions.Length; i++)
                {
                    if (StockDistributions[i].Building.AnimationClass == inBuildingClass)
                    {
                        StockDistributions[i].Queries[inResource] = inIsEnabled;

                        return;
                    }
                }
            }
        }
    }
}
